package org.sag.mallbf;

import org.sag.mallbf.type.DataType;
import org.sag.mallbf.type.ContentType;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ObjReader {

    ArrayList<PolygonObject> objects = new ArrayList<>();

    public void readFromObj(BufferedReader br) throws Exception {
        objects = new ArrayList<>();

        objects.add(new PolygonObject());

        String temp;
        PolygonObject lastObject;
        ObjSegment lastSegment;

        int lineCount = 0;

        while ((temp = br.readLine()) != null) {
            lineCount++;
            ContentType type = ContentType.of(temp);

            if (type != null) {
                String[] r = temp.replaceFirst(type.getAbbr(), "").split(" ");

                lastObject = objects.get(objects.size() - 1);
                lastSegment = lastObject.segments.get(lastObject.segments.size() - 1);

                int length = r.length;

                switch (type) {
                    case V:
                    case VP:
                    case VN:
                    case VT:
                    {
                        double[] v = new double[length];

                        for (int i = 0; i < length; i++) {
                            v[i] = Double.parseDouble(r[i]);
                        }

                        //if any element before reading vertex, append a new segment
                        if (lastSegment.vs.inited) {
                            lastObject.segments.add(new ObjSegment());
                            lastSegment = lastObject.segments.get(lastObject.segments.size() - 1);
                        }

                        //append to lastSegment's VGroup
                        Map<Integer, List<double[]>> lastGroupContent = lastSegment.vs.value;

                        if (lastGroupContent.get(type.getValue()) == null) {
                            lastGroupContent.put(type.getValue(), new ArrayList<double[]>());
                        }
                        List<double[]> lastList = lastGroupContent.get(type.getValue());
                        lastList.add(v);
                    }
                        break;
                    case P:
                        //P v1 v2 v3 ...
                    case L:
                        //L v1 v2 v3 ...
                        //L v1/vt1 v2/vt2 ...
                    case F:
                        //F v1 v2 v3 ...
                        //F v1/vt1 v2/vt2 ...
                        //F v1//vn1 v2//vn2 ...
                        //F v1/vt1/vn1 v2/vt2/vn2 ...
                    {
                        int slashes = r[0].split("/").length - 1;
                        int element;
                        boolean emptyVt = false;
                        if (type == ContentType.P) {
                            element = 1;
                        } else if (type == ContentType.L) {
                            if (slashes == 1) {
                                element = 2;
                            } else {
                                element = 1;
                            }
                        } else if (type == ContentType.F) {
                            if (slashes == 0) {
                                element = 1;
                            } else if (slashes == 1) {
                                element = 2;
                            } else {
                                if (r[0].split("/")[1].equalsIgnoreCase("")) {
                                    element = 3;
                                    emptyVt = true;
                                } else {
                                    element = 3;
                                }
                            }
                        } else {
                            element = 0;
                        }

                        if (lastSegment.gs.size() == 0) {
                            lastSegment.gs.add(new EGroup());
                        }

                        //
                        Map<Integer, List<int[]>> lastGroupContent = lastSegment.gs.get(lastSegment.gs.size() - 1).value;
                        int[] values = new int[length * element];

                        for (int i = 0; i < length; i++) {
                            //read elements
                            String[] sep = emptyVt ? r[i].split("//") : r[i].split("/");
                            if (sep.length != element) {
                                System.out.println("Error: element count not match at line " + lineCount);
                            } else {
                                for (int j = 0; j < element; j++) {
                                    if (emptyVt && j == 1) {
                                        values[j + i * element] = 0;
                                    } else if (emptyVt && j == 2) {
                                        values[j + i * element] = Integer.parseInt(sep[j - 1]);
                                    } else {
                                        values[j + i * element] = Integer.parseInt(sep[j]);
                                    }
                                }
                            }
                        }

                        //read in any element locks the object vertex segment
                        lastSegment.vs.inited = true;

                        if (lastGroupContent.get(type.getValue()) == null) {
                            lastGroupContent.put(type.getValue(), new ArrayList<int[]>());
                        }
                        List<int[]> lastList = lastGroupContent.get(type.getValue());
                        lastList.add(values);
                    }
                        break;
                    case OBJECT:
                        PolygonObject object = new PolygonObject();
                        object.name = r[0];
                        objects.add(object);
                        break;
                    case GROUP:
                        EGroup lastGroup = new EGroup();
                        lastGroup.g = r[0];
                        lastSegment.gs.add(lastGroup);
                        break;
                    default:
                        break;
                }
            }
        }
    }

    public void checkObj() {
        //TODO
    }

    /**
     * Object File Spec
     *
     * --> Version
     * Int16 Type: FILE
     * Int16 Version = 4
     *
     * --> Version Info
     * Int32 Object Count
     *
     * --> Trunk Area
     *
     * @param dos OutputStream to write to
     * @param version version of data spec
     * @throws Exception
     */
    public void exportTo(DataOutputStream dos, int version) throws Exception {
        //write file header

        Utils.putShort(dos, (short) DataType.FILE.getValue(), 0);
        Utils.putShort(dos, (short) version, 0);

        Utils.putInt(dos, objects.size(), 1);

        for (PolygonObject o : objects) {
            o.exportTo(dos, version);
        }

        dos.flush();

        dos.close();
    }
}
